home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / apps / 521 / midipl02 / mf_intp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-17  |  9.2 KB  |  400 lines

  1. /*
  2.  * File: mf_intp.c
  3.  * SGoldthorpe     9-Apr-91
  4.  */
  5.  
  6. /*
  7.  * mf_intp - play standard midi files (sometimes)
  8.  * FREEWARE (C) 1991 Stephen Goldthorpe - Take all you want!  But please it be
  9.  * known if you've made mods.  I don't want a lot of complaints about code I've
  10.  * never even seen before (life's hard enough without all of that)!
  11.  *                        SGoldthorpe.wgc-e@rx.xerox.com
  12.  *                             goldthor@arisia.xerox.com
  13.  */
  14.  
  15. /*
  16.  * REVISION LOG
  17.  * ============
  18.  * 0.1    SGoldthorpe    20-Mar-91    Created for Atari ST / Sozobon C.  It's
  19.  *                    a bit atari specific in places buy i've
  20.  *                    tried to make it UNIX(tm) looking for
  21.  *                    easier porting (if anyone feels brave
  22.  *                    enough to try.
  23.  * 0.2    SGoldthorpe     7-Apr-91    Messed up the code in mf_intp to
  24.  *                    allow type 1 midi files.  Timing is
  25.  *                    still a bit hairy but it plays 80%
  26.  *                    of the files I have OK.
  27.  */
  28.  
  29. #include    <stdio.h>
  30. #include    <types.h>
  31. #include    <time.h>
  32. #include    <string.h>
  33.  
  34. /* and for the atari OS stuff */
  35. #include    <osbind.h>
  36.  
  37. #ifndef    FALSE
  38. #define    FALSE    0
  39. #define    TRUE    (!FALSE)
  40. #endif
  41.  
  42. /* MACRO DEFS */
  43. #define    MAX_TRACKS    32
  44.  
  45. /* EXTERNAL DECLS */
  46. extern char    *app_name;
  47.  
  48. /* GLOBAL VARIABLES */
  49. static double    track_delta[MAX_TRACKS], division, time_per_beat;
  50. static WORD    format, tracks;
  51. static int    finished_tracks;
  52. static clock_t    clock_orig;
  53. static char    *gFile;
  54.  
  55. /* FUNCTION DECLS */
  56. int        mf_intp();
  57. static void    truncated(), parse_error();
  58.  
  59. /* MACRO FUNCTIONS */
  60. /* the error checking may be a bit OTT but I'm gonna do it anyway  (helps
  61.    catch those naughty bugs - and bad files) */
  62. #define GET32BITS(dw,p,l)    dw=(((LONG)(*p)<<24)+            \
  63.                     ((LONG)(*(p+1))<<16)+        \
  64.                     (((LONG)*(p+2))<<8)+        \
  65.                     (LONG)(*(p+3)));        \
  66.                 if(l<4)                    \
  67.                 {    truncated();            \
  68.                     return(-1);            \
  69.                 };                    \
  70.                 p += 4; l -= 4
  71.  
  72. #define    GET16BITS(w,p,l)    w=(((WORD)(*p)<<8)+(WORD)*(p+1));    \
  73.                 if(l<2)                    \
  74.                 {    truncated();            \
  75.                     return(-1);            \
  76.                 }                    \
  77.                 p += 2; l -= 2
  78.  
  79. #define    GET8BITS(b,p,l)        b = *(p)++;                \
  80.                 if(--l<0)                \
  81.                 {    truncated();            \
  82.                     return(-1);            \
  83.                 }
  84.  
  85. #define    GETVARLEN(dw,p,l)    for(dw=(LONG)(*p)&0x7f;(*(p)++)&0x80;)    \
  86.                 {    if(--l<0)            \
  87.                     {    truncated();        \
  88.                         return(-1);        \
  89.                     };                \
  90.                     dw <<=7;            \
  91.                     dw |= (LONG)(*p)&0x7f;        \
  92.                 };                    \
  93.                 if(--l<0)                \
  94.                 {    truncated();            \
  95.                     return(-1);            \
  96.                 }
  97.  
  98. #define    CHECKLEFT(l,v)        if(l<v)                    \
  99.                 {    truncated();            \
  100.                     return(-1);            \
  101.                 }
  102.  
  103. #define    SEND(b)            Bconout(3,b)
  104.  
  105. /* FUNCTION DEFS */
  106. int    mf_intp(buffer, file, len)
  107. char        *file;
  108. BYTE        *buffer;
  109. unsigned int    len;
  110. {    BYTE    *track_pos[MAX_TRACKS];
  111.     int    track_left[MAX_TRACKS];
  112.     int    i;
  113.     int        track_finished[MAX_TRACKS];
  114.     BYTE        *pos=buffer,*next;
  115.     BYTE        running_status;
  116.     WORD        w;
  117.     LONG        dw,delta;
  118.     double        clock_delta;
  119.     unsigned int    left=len;
  120.  
  121.     gFile=file;
  122.  
  123.     /* check header */
  124.     if((left<4)||(strncmp("MThd",(char*)pos,4)!=0))
  125.     {    (void)fprintf(stderr,
  126.             "%s: %s is not a midi file\n",app_name,file);
  127.         return(-1);
  128.     };
  129.     pos += 4; left -= 4;
  130.  
  131.     /* find address of next chunk */
  132.     GET32BITS(dw,pos,left);
  133.     next=pos+dw;
  134. #ifdef    DEBUG
  135.     (void)printf(
  136.         "pos is %lx len is %ld next would be %lx\n",pos-buffer,dw,
  137.         next-buffer);
  138. #endif
  139.  
  140.     /* get file format */
  141.     GET16BITS(format,pos,left);
  142.     switch (format)
  143.     {    case 0:
  144.         case 1:
  145.             /* OK we accept formats 0 and 1 */
  146.             break;
  147.         default:
  148.             /* but we don't do any others */
  149.             (void)fprintf(stderr,
  150.                 "%s: can't play %s, midi file type %d\n",
  151.                 app_name, file,format);
  152.             return(-1);
  153.     };
  154.  
  155.     /* get number of tracks */
  156.     GET16BITS(tracks,pos,left);
  157.  
  158.     /* check tracks in range */
  159.     if(tracks > MAX_TRACKS)
  160.     {    (void)fprintf(stderr,
  161.             "%s: %s has too many tracks (%d allowed).\n",app_name,
  162.              file, tracks, MAX_TRACKS);
  163.         return(-1);
  164.     };
  165.  
  166.     /* get division - this is the division of a quarter note or
  167.        if negative is frame based. */
  168.     GET16BITS(w,pos,left);
  169.     division=(double)w;
  170.  
  171.     /* I don't suport the frame stuff yet! */
  172.     if(division < 0)
  173.     {    (void)fprintf(stderr,
  174.             "%s: file %s - don't support framed based files yet!",
  175.             app_name, file);
  176.         return(-1);
  177.     };
  178. #ifdef    DEBUG
  179.     (void)printf("division = %f\n",division);
  180. #endif
  181.  
  182.     time_per_beat = (double)(CLK_TCK * 60.0)/120.0; /* default 120bpm */
  183. #ifdef    DEBUG
  184.     (void)printf("time per beat (1/200 sec) %f\n",time_per_beat);
  185. #endif
  186.  
  187.     /* do some initialisation, track finding etc */
  188.     running_status = 0xfe;
  189.     finished_tracks = 0;
  190.     for(i=0;i<tracks;i++)
  191.     {    CHECKLEFT(left,(next-pos));
  192.         left -= next-pos; pos = next;
  193.  
  194.         /* track start  */
  195.         if((left<4)||(strncmp("MTrk",(char*)pos,4)!=0))
  196.         {    (void)fprintf(stderr,
  197.                 "%s: %s parse error, track expected\n",app_name,
  198.                 file);
  199. #ifdef    DEBUG
  200.             (void)printf(
  201.                 "posn %lx, bytes around (-3..3) %02x %02x %02x \
  202. %02x %02x %02x %02x\n",pos-buffer,*(pos-3),*(pos-2),*(pos-1),*pos,*(pos+1),
  203.                 *(pos+2),*(pos+3));
  204. #endif
  205.             return(-1);
  206.         };
  207.         pos += 4; left -= 4;
  208.     
  209.         /* find address of next chunk */
  210.         GET32BITS(dw,pos,left);
  211.         *(track_pos+i) = pos;
  212.         next = pos+dw;
  213.         *(track_left+i) = (int)dw;
  214. #ifdef    DEBUG
  215.         (void)printf("pos is %lx len is %ld next would be %lx\n",
  216.             pos-buffer,dw,next-buffer);
  217. #endif
  218.  
  219.         /* get initial delta time */
  220.         GETVARLEN(dw,*(track_pos+i),*(track_left+i));
  221.         *(track_delta+i)=(double)dw;
  222.         *(track_finished+i)=FALSE;
  223.     }
  224.  
  225.     /* let the user know what's happening */
  226.     (void)printf("playing '%s' with %d tracks\n",file,tracks);
  227.  
  228.     /* get start time */
  229.     clock_orig=clock();
  230.     
  231.     /* dispatcher - needs some work with timing etc doubles aren't really
  232.        accurate enough (size wise) */
  233.     while (finished_tracks != tracks)
  234.     {    BYTE    event;
  235.         clock_delta = (double)(clock()-clock_orig)*division/
  236.             time_per_beat;
  237.         for(i=0;i<tracks;i++)
  238.         {    if(!*(track_finished+i) & (clock_delta>
  239.                 *(track_delta+i)))
  240.             {    GET8BITS(event,*(track_pos+i),*(track_left+i));
  241.  
  242.                 /* parse event */
  243.                 switch (event)
  244.                 {    /* meta-events */
  245.                     case 0xff:
  246.                     {    BYTE    type;
  247.                         LONG    length;
  248.                         GET8BITS(type,*(track_pos+i),
  249.                             *(track_left+i));
  250. #ifdef    DEBUG
  251.                         (void)printf("meta-event %02x ",
  252.                             type);
  253. #endif
  254.                         GETVARLEN(length,*(track_pos+i),
  255.                             *(track_left+i));
  256. #ifdef    DEBUG
  257.                         (void)printf("length %ld\n",
  258.                             length);
  259. #endif
  260.             switch(type)
  261.             {    /* u-sec tempo set (not sure if this is
  262.                    correct, documents are not clear enough
  263.                    about this) */
  264.                 case 0x51:
  265.                 {    LONG    t;
  266.                     BYTE    c;
  267.                     GET8BITS(c,*(track_pos+i),
  268.                         *(track_left+i));
  269.                     t=(LONG)c<<16;
  270.                     GET8BITS(c,*(track_pos+i),
  271.                         *(track_left+i));
  272.                     t += (LONG)c<<8;
  273.                     GET8BITS(c,*(track_pos+i),
  274.                         *(track_left+i));
  275.                     t += (LONG)c;
  276. #ifdef    DEBUG
  277.                     (void)printf("usec %ld ",t);
  278. #endif
  279.                     t=(t*(LONG)CLK_TCK)/1000000L;
  280.                     time_per_beat = (double)t;
  281. #ifdef    DEBUG
  282.                     (void)printf("our %f\n",time_per_beat);
  283. #endif
  284.                     break;
  285.                 };
  286.  
  287.                 /* end of track - can't be bothered to check
  288.                    left == 0 too */
  289.                 case 0x2f:
  290.                     *(track_finished+i)=TRUE;
  291.                     finished_tracks++;
  292.                     break;
  293.  
  294.                 /* ignore rest for now */
  295.                 default:
  296.                     CHECKLEFT(*(track_left+i),(int)length);
  297.                     *(track_pos+i) += length;
  298.                     *(track_left+i) -= length;
  299.                     break;
  300.             };
  301.                     break;
  302.                     }
  303.  
  304.                     /* sysex events */
  305.                     case 0xf0:
  306.                     case 0xf7:
  307.                     {    LONG    length;
  308.                         GETVARLEN(length,*(track_pos+i),
  309.                             *(track_left+i));
  310.                         CHECKLEFT(*(track_left+i),
  311.                             (int)length);
  312.                         /* ignoring these too */
  313.                         *(track_pos+i) += length;
  314.                         *(track_left+i) -= length;
  315.                         break;
  316.                     };
  317.  
  318.                     /* midi events */
  319.                     default:
  320.         {    switch(event & 0xf0)
  321.             {    /* 3 byte events */
  322.                 case 0x90:
  323.                 case 0x80:
  324.                 case 0xa0:
  325.                 case 0xb0:
  326.                 case 0xe0:
  327.                 {    BYTE    c;
  328.                     SEND(event);
  329.                     GET8BITS(c,*(track_pos+i),
  330.                         *(track_left+i));
  331.                     SEND(c);
  332.                     GET8BITS(c,*(track_pos+i),
  333.                         *(track_left+i));
  334.                     SEND(c);
  335.                     running_status = event;
  336.                     break;
  337.                 };
  338.                 /* 2 byte events */
  339.                 case 0xc0:
  340.                 case 0xd0:
  341.                 {    BYTE    c;
  342.                     SEND(event);
  343.                     GET8BITS(c,*(track_pos+i),
  344.                         *(track_left+i));
  345.                     SEND(c);
  346.                     running_status = event;
  347.                     break;
  348.                 };
  349.                 default:
  350.                 {    /* running status - hope a golbal one
  351.                        is OK */
  352.                     if((event&0x80)==0)
  353.                     {    SEND(event);
  354. #ifdef    DEBUG
  355.                         (void)printf(
  356.                             "running stat (%02x) \
  357. %02x\n",running_status,event);
  358. #endif
  359.                         switch(running_status & 0xf0)
  360.                         { /* 3 byte events */
  361.                             case 0x90:
  362.                             case 0x80:
  363.                             case 0xa0:
  364.                             case 0xb0:
  365.                             case 0xe0:
  366.                             {    BYTE c;
  367.                         GET8BITS(c,*(track_pos+i),
  368.                             *(track_left+i));
  369.                                 SEND(c);
  370.                             };
  371.                         };
  372.                     };
  373.  
  374.                     /* ignoring other 0xf? events -
  375.                        naughty aren't I */
  376.                 };
  377.             };
  378.         };
  379.                 };
  380.  
  381.                 /* get delta time  - but only if we're still
  382.                    going on this track */
  383.                 if(!*(track_finished+i))
  384.                 {    GETVARLEN(delta,*(track_pos+i),
  385.                         *(track_left+i));
  386.                     *(track_delta+i) += (double)delta;
  387.                 };
  388.             };
  389.         };
  390.     };
  391.     return(0);
  392. };
  393.  
  394. /* general error messages - I got fed up typing these over and over again */
  395. static void    truncated()
  396. {    (void)fprintf(stderr,"%s: %s truncated\n",app_name,gFile);
  397. };
  398.  
  399. /* THAT'S ALL FOLKS! */
  400.